package com.tencent.wepay.service.bill;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.lang.StringUtils;

import com.tencent.wepay.WepayManager;
import com.tencent.wepay.constant.enums.BillTypeEnum;
import com.tencent.wepay.constant.enums.TradeTypeEnum;
import com.tencent.wepay.handler.xml.XStreamHandler;
import com.tencent.wepay.protocol.bill.domain.Bill;
import com.tencent.wepay.protocol.bill.domain.Detail;
import com.tencent.wepay.protocol.bill.domain.Sum;
import com.tencent.wepay.protocol.bill.request.BillDownloadRequest;
import com.tencent.wepay.protocol.http.HttpRequest;
import com.tencent.wepay.spi.WepayRepository;
import com.thoughtworks.xstream.annotations.XStreamAlias;

public class BillService {
	private final static String url="https://api.mch.weixin.qq.com/pay/downloadbill";
	public static Bill download(String date,BillTypeEnum billType,TradeTypeEnum tradeType) throws Exception{
		WepayRepository repository=null;
		if(TradeTypeEnum.JSAPI.equals(tradeType)){
			repository=WepayManager.getJsapiRepository();
		}
		else if(TradeTypeEnum.APP.equals(tradeType)){
			repository=WepayManager.getAppRepository();
		}	
		BillDownloadRequest request=new BillDownloadRequest();
		request.setAppid(repository.getAppid());
		request.setKey(repository.getKey());
		request.setMchId(repository.getMchId());
		request.setBillType(billType);
		request.setBillDate(date);
		String result=HttpRequest.getInstance().post(url, request.toXML());
		Boolean failed=result.indexOf("<return_code>")==-1?false:result.substring(result.indexOf("<return_code>"), result.indexOf("</return_code>")).contains("FAIL");
		if(failed){
			return new Bill();
		}
		Bill bill=parse(result);
		return bill;
	}
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Bill parse(String result){
		String[] rows=result.replaceAll("`", "").split("\r\n");
		Class clz=Detail.class;
		Field[] fields=mapping(rows[0], clz);
		Bill bill=new Bill();
		for (int i=1;i<rows.length;i++) {
			String row=rows[i];
			String[] cols=row.split(",");
			if(cols.length!=fields.length){
				clz=Sum.class;
				fields=mapping(row, clz);
				continue;
			}
			try {
				Object object=clz.newInstance();
				for (int j=0; j<cols.length;j++) {
					Field field=fields[j];
					PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clz);
					Method method=pd.getWriteMethod();
					Object o=null;
					Class type=field.getType();
					if(StringUtils.isBlank(cols[j])){
						
					}
					else if(cols[j].endsWith("%")){
						o=NumberFormat.getPercentInstance().parse(cols[j]);
						o=ConvertUtils.convert(o, type);
					}
					else if(type.equals(String.class)){
						o=cols[j];
					}
					else if(type.getSuperclass().equals(Enum.class)){
						o=Enum.valueOf(type, cols[j]);
					}
					else{
						o=ConvertUtils.convert(cols[j], field.getType());
					}
					method.invoke(object,o);		
				}	
				if(object instanceof Detail){
					bill.addDetail((Detail) object);
				}
				else if(object instanceof Sum){
					bill.setSum((Sum) object);
				}
			} catch (IntrospectionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
			
		}
		return bill;
	}
	@SuppressWarnings("rawtypes")
	private static Field[] mapping(String head,Class clz){
		List<String> headerList=Arrays.asList(head.split(","));
		Field[] mapFields=new Field[headerList.size()];
		Field[] fields=clz.getDeclaredFields();
		for (Field field : fields) {
			field.setAccessible(true);
			XStreamAlias xsAlias=field.getAnnotation(XStreamAlias.class);
			if(xsAlias==null)
				continue;
			String value=xsAlias.value();
			int location=headerList.indexOf(value);
			mapFields[location]=field;
		}
		return mapFields;
	}
}
